
//
// 101020 - AH - Created.
// 101021a- AH - match(List<AttributeObject>) no longer returns AttributeObject.
// 101021b- AH - Now processing of extracting keywords from input is done here.
// 101214 - AH - Renamed methods to be true to Laetitia's concepts.
// 110108 - AH - rinse().
// 110109 - AH - (Hector) Introducing the RetrievalEvent.
// 110110a- AH - getInput(List<AttributeObject>).
// 110110b- AH - (Garage *) Minor java doc fix.
// 110112a- AH - (Fantoft) EMPTY_ARRAY.
// 110112b- AH - (Fantoft) toString().
// 110116 - AH - Missing java doc.
// 110117 - AH - (Hector) Renamed getInput(List<AttributeObject>) to
//               getToken(List<AttributeObject>) to be true to terminology.
// 110709 - AH - (Garage) getOnotology().
// 120116 - AH - (Huset) Renamed from Spoke, think Ontology found home.
// 120228 - AH - (Zen *) getSoekratNames(List<SoekratName>).
// 120401 - AH - (Garage) stamp property.
// 120519 - AH - (Aura) Semantic effect.
//

package org.soekrat.engine.wheel.ontology;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.soekrat.AttributeObject;
import org.soekrat.engine.SoekratName;
import org.soekrat.engine.ontology.OntologyDatastructure;
import org.soekrat.engine.orb.Soekrat2Orb;
import org.soekrat.engine.visitor.TokenizedLine;
import org.soekrat.engine.wheel.Wheel;
import org.soekrat.engine.wheel.retrieval.RetrievalEvent;
import org.soekrat.engine.wheel.retrieval.RetrievalEventType;

/**
 * <p>
 *   Base class for spokes in a
 *   {@link Wheel}.
 * </p>
 * @author Laetitia Le Chatton (LLC) 
 * @author Arne Halvorsen (AH) 
 */
public abstract class Ontology 
{
  private final Wheel m_wheel;
  
  private String m_stamp="string";
  
  private final String m_semanticEffect;
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param wheel {@link Wheel} {@code this} 
   *              {@link Ontology} belong to.
   */
  protected Ontology(Wheel wheel, String semanticEffect)
  {
    if (wheel==null)
    {
      throw new NullPointerException("wheel");
    }
    if (semanticEffect==null)
    {
      throw new NullPointerException("semanticEffect");
    }
    
    m_wheel=wheel;
    m_semanticEffect=semanticEffect;
  }
  
  /**
   * <p>
   *   Gets the name of the semantic effect {@code this} represents. 
   * </p>
   * @return Name of effect.
   */
  public String getSemanticEffect(){ return m_semanticEffect; }
  
  /**
   * <p>
   *   Gets stamp value that what is retrieved from {@code this} 
   *   {@link Ontology} is stamped with. 
   * </p>
   * <p>
   *   Clients will use this value to decide how result is to be presented to
   *   the world.
   * </p>
   * @return Stamp value.
   */
  public final String getStamp(){ return m_stamp; }
  
  /**
   * <p>
   *   Sets stamp value that what is retrieved from {@code this} 
   *   {@link Ontology} is stamped with. 
   * </p>
   * <p>
   *   Clients will use this value to decide how result is to be presented to
   *   the world.
   * </p>
   * @return Stamp value.
   */
  public final void setStamp(String stamp)
  {
    if (stamp==null)
    {
      throw new NullPointerException("stamp");
    }
    
    m_stamp=stamp;
    
    stamp();
  }
  
  protected abstract void stamp();
  
  /**
   * <p>
   *   Gets name that should identify this possible to end user in GUI.
   * </p>
   * @return Name.
   */
  public abstract String getName();
  
  /**
   * <p>
   *   Gets
   *   {@link SoekratName}s {@code this} stores.
   * </p>
   * @param names List to add names to (keeps old content). May be {@code null}.
   * @return Passed list {@code names} with added names or new list with 
   *         {@code this}' names if {@code null} passed.
   */
  public abstract List<SoekratName> getSoekratNames(List<SoekratName> names);
  
  /**
   * <p>
   *   Gets
   *   {@link Wheel} {@code this} belongs to.
   * </p>
   * @return {@code Wheel}.
   */
  public final Wheel getWheel(){ return m_wheel; }
  
  /**
   * <p>
   *   Gets
   *   {@link OntologyDatastructure} {@code this} manages.
   * </p>
   * @return {@link OntologyDatastructure}.
   */
  public abstract OntologyDatastructure getOntologyDatastructure();
  
  /**
   * <p>
   *   Do retrieval (scan) and prepare result for end user or as input to
   *   another ontology.
   * </p>
   * @param orb {@link Soekrat2Orb} that has input and get new data
   *           this may produce.
   * @return {@link RetrievalEvent} that is the last produced by this.
   */
  public final RetrievalEvent scanAndPaint(Soekrat2Orb orb)
  {
    if (orb==null)
    {
      throw new NullPointerException("ce");
    }
    
    Integer pc=(Integer)orb.getAttribute("pass-count");
    int passCount=(pc==null) ? 0 : pc;
    
    Integer ontologyCount=(Integer)orb.getAttribute("ontology-count");
    ontologyCount=(ontologyCount==null) ? 1 : ontologyCount++;
    orb.setAttribute("ontology-count", ontologyCount);
    
    TokenizedLine input=(TokenizedLine)orb.getAttribute("input");
    if (input==null)
    {
      throw new IllegalArgumentException("ce missing attribute input");
    }
    
    List<AttributeObject> paint=new ArrayList<AttributeObject>();
    
    //System.err.println("      ONTOLOGY: "+getName()+" input : "+input);
    
    RetrievalEvent re=scanAndPaint(input, paint);
    
    RetrievalEvent last=re.getLast();
    
    if (last.getType()!=RetrievalEventType.PASS)
    { 
      //System.err.println("      ONTOLOGY: "+getName()+" painted : "+
      //  AttributeObject.toString(paint));
      
      TokenizedLine replaced=getTokens(paint);
      
      orb.setAttribute("input", replaced);
      orb.setAttribute("paint", paint);
      
      orb.painted(this);
    }
    else
    {
      //System.err.println("      ONTOLOGY: "+getName()+" passed");
      passCount++;
    }
    
    orb.setAttribute("pass-count", passCount);
    
    return re;
  }
  
  /**
   * <p>
   *   Perform retrieval of information.
   * </p>
   * @param input Tokens extracted from visitor's input.
   * @param paint Where retrieved information is added.
   * @return {@link RetrievalEvent} that is first in sequence that identifies 
   *         steps performed by retrieval algorithm.
   */
  public final RetrievalEvent scanAndPaint(TokenizedLine input, 
    List<AttributeObject> paint)
  {
    if (input==null)
    {
      throw new NullPointerException("tokens");
    }
    if (paint==null)
    {
      throw new NullPointerException("paint");
    }
    
    Object matched=scan(input);
    return paint(matched, paint);
  }
  
  /**
   * <p>
   *   Performs first step of producing output from input, finding matches in
   *   knowledge elements {@code this} spoke organizes. 
   * </p>
   * @param keywords Keywords found in input to match.
   * @return Result of scan.
   */
  protected abstract Object scan(TokenizedLine keywords);
  
  /**
   * <p>
   *   Performs second step of producing output from input, processing scanned
   *   information found by
   *   {@link #scan(Set)}.
   * </p>
   * @param scan  Match information.
   * @param paint Output.
   * @return {@link RetrievalEvent} for this.
   */
  protected abstract RetrievalEvent paint(Object scan, 
    List<AttributeObject> paint);
  
  /**
   * <p>
   *   {@link Ontology}s that need to rinse data before retrieval of data override
   *   this method.
   * </p>
   */
  public void rinse(){}
  
  /**
   * <p>
   *   Gets token to pass to another
   *   {@link Ontology} given what has been retrieved by {@code this}
   *   {@code Spoke}.
   * </p>
   * @param paint What has been retrieved by {@code this}
   *              {@code Spoke}.
   * @return Tokens.
   */
  public abstract TokenizedLine getTokens(List<AttributeObject> paint);
  
  @Override
  public String toString(){ return getName(); }
  
  /**
   * <p>
   *   The empty array shared.
   * </p>
   */
  public final static Ontology[] EMPTY_ARRAY=new Ontology[0];
  
}
